Skip to content

feat(edit): add, edit and remove PDF link annotations (#35) - #55

Open
nonamexishere wants to merge 2 commits into
McanKul:developmentfrom
nonamexishere:feat/35-pdf-link-annotations
Open

feat(edit): add, edit and remove PDF link annotations (#35)#55
nonamexishere wants to merge 2 commits into
McanKul:developmentfrom
nonamexishere:feat/35-pdf-link-annotations

Conversation

@nonamexishere

Copy link
Copy Markdown
Contributor

Summary

Implements #35: manage standard URI and in-document GoTo link annotations on Edit PDF.

This is a different job from #54 (#34 publish gate). Save still uses that gate, so this branch is stacked on it.

Depends on #54. Please merge #54 first. After that this PR rebases to link-only commits. Merging this before #54 would also land the validation gate.

In scope

  • Detect existing /Link annotations whose action is allowlisted URI (https / http / mailto) or in-document GoTo
  • Show those hotspots on the canvas (dashed rect, same Crop/Rotate space as stamps)
  • Create, edit, and remove supported links
  • Leave JS / Launch / file / remote-GoTo and unrelated annotations unchanged
  • Save writes real /Annots, then the test(editor): validate and round-trip edited PDF outputs before publish #34 qpdf --check + snapshot gate
  • Original file is never overwritten

Out of scope

Why

Overlay stamps are not PDF links. Viewers need /Subtype /Link + /A.

Validation

  • npm run typecheck
  • npm test (180)
  • cargo test --manifest-path src-tauri/Cargo.toml --lib (120)
  • Manual Edit PDF with local QA PDFs (URI, GoTo, mixed, rotate+crop, clean)

Privacy Checklist

  • This keeps OffPDF usable offline.
  • This does not upload, log, or transmit user files. URIs are not fetched.
  • New dependencies or bundled binaries have compatible licenses.

@McanKul McanKul left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the substantial work here. I found two fail-closed issues that need fixing before merge:

  • list_link_annots returns an empty list above 400 MB and silently stops at 5,000 links. The UI treats that as a complete hydration, while Save can then remove supported links that were never loaded. Please return/surface an actionable error or preserve every unhydrated annotation, with regression tests for both limits.
  • A no-op save deletes and recreates every supported link. That drops existing annotation properties such as /Border, /H, /F, /AP, /QuadPoints, and /Contents, which conflicts with #35’s no-op preservation requirement. Please preserve untouched supported annotation objects (or round-trip their fields) and add a no-op structure test.

Also surface hydration failures instead of leaving Save disabled behind “Still reading links”. After #54 lands, please rebase this branch onto current development; then we can re-review the link-specific diff.

@nonamexishere

Copy link
Copy Markdown
Contributor Author

Thanks — the review items should be addressed on 9d0cb2b (rebased onto current development after #54; old gate commits dropped).

  • 400 MiB / 5,000: list_link_annots returns an actionable AppError (FILE_TOO_LARGE / TOO_MANY_LINKS), never Ok([]) or a silent Ok(5000). Caps are unchanged.
  • Save after incomplete hydrate: skip apply_link_annots so dest supported links are not deleted. Stamp-only Save is allowed. Adding/editing/removing a link on a file that failed list returns the same list error.
  • No-op Save: unchanged rect+action keeps the dest annot object (/Border, /H, /F, /AP, /QuadPoints, /Contents, and the rest).
  • Hydrate UI: real AppError is toasted; Save is not stuck on “Still reading links.”

CI should run on this head.

@nonamexishere
nonamexishere force-pushed the feat/35-pdf-link-annotations branch from 7fa63ea to 9d0cb2b Compare August 26, 2026 16:03
@nonamexishere

Copy link
Copy Markdown
Contributor Author

CI has not started on this head (9d0cb2b) — GitHub reports no checks at all, not a red run.

The review fixes are on that commit (rebased onto development after #54). Locally: cargo test --lib pdf_engine::edit_links (18) and the owner items above.

On this repo, Actions looks stuck since ~15:04 UTC (jobs sitting in queued, and the #59 merge into development is startup_failure with no jobs). If you see Approve and run workflows on this fork PR, please kick that when the runner queue is healthy — another force-push from us will not create a run until then.

No rush if you are already looking at the code.

Detect existing allowlisted URI and in-document GoTo annotations,
show them on the Edit PDF canvas, and write real /Link dictionaries
on save after overlay. Unsupported actions are left in place.
Uses the McanKul#34 staged-output gate. Fixes McanKul#35.
list_link_annots returns AppError above 400 MiB or 5000 supported links
instead of a list that looks complete. No-op Save keeps dest annot
objects. Hydrate errors toast and still allow stamp-only Save.
@nonamexishere
nonamexishere force-pushed the feat/35-pdf-link-annotations branch from 9d0cb2b to 89ee533 Compare August 26, 2026 16:20

@McanKul McanKul left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — the original review items are fixed, and both GitHub CI and my local frontend/Rust runs pass. I found three remaining blockers:

  • doc.objects.length === 0 blocks Save after deleting the last existing link, so remove-all never reaches the backend. Please allow that case and add a regression test.
  • Partial hydration is not fail-closed for multi-file edits. If file A fails link listing but a link on successfully hydrated file B is edited or deleted, the frontend allows Save; linksComplete=false then makes should_rewrite_supported_links skip all link writes, so the edit is silently discarded. Please preserve/apply per source or block link mutations while hydration is incomplete, with a mixed-source edit/delete test.
  • The limits conflict: listing accepts up to 5,000 links, while validate_doc rejects more than 500 total editor objects. A PDF with 501–5,000 supported links hydrates successfully but cannot save. Please align or separate link and paint-object limits and test the boundary.

Once these are covered, this should be ready for another pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants